package com.darkevil.jdspider.service

import com.darkevil.jdspider.entity.Category
import com.darkevil.jdspider.entity.Product
import com.darkevil.jdspider.mapper.CategoryMapper
import com.darkevil.jdspider.utils.HttpClientUtil
import org.jsoup.Jsoup
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.json.JacksonJsonParser
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Service
import java.math.BigDecimal
import java.util.*
import kotlin.math.abs

@Service
class ProductService {
    private val logger = LoggerFactory.getLogger(ProductService::class.java)
    @Autowired
    private lateinit var httpclient: HttpClientUtil
    @Autowired
    private lateinit var categoryMapper: CategoryMapper
    @Autowired
    private lateinit var queueService: QueueService


    @Async("taskExecutor")
    fun getProduct(category: Category) {
        logger.info("线程启动-开始抓取类别[${category.name}]数据  活动线程数${Thread.activeCount()}")
        var respHtml: String? = null
        val url = category.url
        var count = 0

        respHtml = httpclient.sendHttpGet(url!!)
        for (pageNo in 1..getPageCount(respHtml ?: "")) {
            try {
                val tmpUrl = "$url&page=$pageNo&delivery_daofu=0&stock=0&sort=sort_winsdate_desc&trans=1"
                var repeat = 0
                do {
                    respHtml = httpclient.sendHttpGet(tmpUrl)
                    val tmpPageNo = getCurrPageNo(respHtml ?: "")
                    repeat++
                    if (pageNo != tmpPageNo) {
                        logger.debug("页码不一致 重试次数${repeat - 1} url=$tmpUrl 当前页码：$pageNo  实际页码：$tmpPageNo 实际url:${getCurrPageUrl(respHtml
                                ?: "")}")
                    }
                    if (repeat > 80) {
                        logger.warn("重试次数过多 重试次数${repeat - 1} url=$tmpUrl 当前页码：$pageNo  实际页码：$tmpPageNo 实际url:${getCurrPageUrl(respHtml
                                ?: "")}")
                    }
                    if(repeat>1000){
                        break
                    }

                } while (pageNo != tmpPageNo)    // 只要页码与实际页面页码不一致就重复执行
//                logger.debug("收到响应：$respHtml")
                val productTmpList = parseProduct(respHtml ?: "", category)
                // 商品数据持久化
                count += productTmpList.size
                queueService.addProductQueue(productTmpList)
            } catch (e: Exception) {
                logger.error("类别[${category.name}]的商品获取异常，请求:[${category.url}]", e)
            }
        }
        // 更新类别更新时间
        category.updateDate = Date()
        categoryMapper.replace(category)

        logger.info("类别[${category.name}]爬取完成，共${count}个商品")
    }

    /**
     * 解析页面获取产品列表
     */
    private fun parseProduct(html: String, category: Category): MutableList<Product> {
        val doc = Jsoup.parse(html)
        val retList = arrayListOf<Product>()
        doc.select(".j-sku-item").forEach {
            val sku = it.attr("data-sku")
            val url = "https://item.jd.com/$sku.html"
            val itemName = it.select("em").text()
            val product = Product(id = sku, sku = sku, url = url, name = itemName, category = category)
            retList.add(product)
            logger.debug(product.toString())
        }
        getProductListPrice(retList)
        logger.debug(retList.toString())
        return retList
    }

    /**
     * 获取商品价格
     */
    private fun getProductListPrice(productList: MutableList<Product>) {
        var skuIds = ""
        var count = 0
        val pduid = abs(UUID.randomUUID().hashCode())
        var url = ""
        var respStr: String? = ""
        val jsonParser = JacksonJsonParser()
        val respList = arrayListOf<LinkedHashMap<String, String>>()
        productList.forEach {
            if (skuIds.isNotEmpty()) {
                skuIds += ","
            }
            skuIds += "J_${it.sku}"
            count++
            // 价格请求又100条限制
            if (count >= 100) {
                url = "https://p.3.cn/prices/mgets?pdtk=&pduid=$pduid&skuIds=$skuIds"
                respStr = httpclient.sendHttpGet(url)
                if (respStr?.contains("error") == true) {
                    logger.error("获取价格失败：url=$url")
                    return
                }
                respList.addAll(jsonParser.parseList(respStr) as MutableList<LinkedHashMap<String, String>>)
                count = 0
                skuIds = ""
            }

        }
        if (skuIds.isNotBlank()) {
            url = "https://p.3.cn/prices/mgets?pdtk=&pduid=$pduid&skuIds=$skuIds"
            respStr = httpclient.sendHttpGet(url)
            if (respStr?.contains("error") == true) {
                logger.error("获取价格失败：url=$url")
                return
            }
            respList.addAll(jsonParser.parseList(respStr) as MutableList<LinkedHashMap<String, String>>)
        }
        productList.forEach {
            val tmpList = respList.filter { respMap -> respMap["id"] == "J_" + it.id }
            if (tmpList.isNotEmpty()) {
                it.price = BigDecimal(tmpList[0]["p"])
            } else {
                logger.error("没有获取到价格 目标id:J_${it.id}  请求url：$url")
            }
        }
    }

    /**
     * 解析页面总页数
     */
    private fun getPageCount(html: String): Int {
        val doc = Jsoup.parse(html)
        val count = doc.select(".p-skip>em>b").text()
        return if (count.isNotBlank()) {
            count.toInt()
        } else {
            1
        }
    }

    /**
     * 获取当前页面的页码
     */
    private fun getCurrPageNo(html: String): Int {
        val doc = Jsoup.parse(html)
        val pageNo = doc.select(".page .curr").not(".hide").text()
        return if (pageNo.isNullOrBlank()) {
            1
        } else {
            pageNo.toInt()
        }
    }

    /**
     * 获取当前页面的url
     */
    private fun getCurrPageUrl(html: String): String {
        val doc = Jsoup.parse(html)
        return doc.select(".page .curr").not(".hide").attr("href")
    }
}